package com.service;

import com.util.excelUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;


@Service
public class DataAnalyseOperator {

    private static final String temp_path = new File("").getAbsolutePath() + "/../webapps/ATTRMaterialDatabase/WEB-INF/Python/train.py";
    private static final String PYTHON_TRAINING_SCRIPT_PATH = new String(temp_path);
    private static final String PYTHON_PREDICTING_SCRIPT_PATH = new File("").getAbsolutePath() + "/../webapps/ATTRMaterialDatabase/WEB-INF/Python/predict.py";
    private static Logger logger = Logger.getLogger(String.valueOf(DataAnalyseOperator.class));
    private volatile static AtomicInteger version = new AtomicInteger(0);
    private static ReentrantLock lock = new ReentrantLock();

    public HashMap<String, String> validate(CommonsMultipartFile file) throws IOException, InvalidFormatException {
        lock.lock();
        // 存储到 文件中
        String tmp_path = file.getOriginalFilename();
        File excel_file = null;

        excel_file = new File(System.currentTimeMillis() + "_" + Thread.currentThread().getName() + "_" + tmp_path);
        System.out.println("excel_file.getAbsolutePath() ---- " + excel_file.getAbsolutePath());
        file.transferTo(excel_file);

        // Excel 解析对象
        FileInputStream in = new FileInputStream(excel_file);
        XSSFWorkbook wb = new XSSFWorkbook(in);
        XSSFSheet sheet = wb.getSheetAt(0);
        XSSFRow row = null;
        XSSFCell cell = null;

        HashMap<String, String> ret = new HashMap<>();

        // header 解析
        row = sheet.getRow(0);
        ArrayList<String> headers = new ArrayList<>();
        int i = 0;
        while (true) {
            cell = row.getCell(i++);
            String temp = excelUtil.getCellStrValue(cell);
            if (temp == null || temp.equals("") || temp.equals("null")) {
                break;
            } else {
                headers.add(temp);
            }
        }

        ret.put("cols", StringUtils.join(headers, ","));
        // 数据校验（1.不能有空值 2.不能有非数值型数据）
        if (sheet.getPhysicalNumberOfRows() < 64) {
            ret.put("status", "failed");
            ret.put("msg", "data rows at least 64, file exist " + sheet.getPhysicalNumberOfRows() + " rows");
            return ret;
        }
        for (int r = 1; r < sheet.getPhysicalNumberOfRows(); r++) {
//            System.out.println("");
            for (int c = 0; c < headers.size(); c++) {
                cell = sheet.getRow(r).getCell(c);
//                System.out.print(excelUtil.getCellStrValue(cell) + "\t");
                if (excelUtil.getCellStrValue(cell).equals("")) {
                    ret.put("status", "failed");
                    ret.put("msg", "data can't be empty ,row: " + (r + 1) + ",column:" + (c + 1));
                    return ret;
                }
                try {
                    Double.parseDouble(excelUtil.getCellStrValue(cell));
                } catch (Exception e) {
                    ret.put("status", "failed");
                    ret.put("msg", "not numeric data,row:" + (r + 1) + ",column:" + (c + 1) + ", value:" + excelUtil.getCellStrValue(cell));
                    return ret;
                }
            }
        }

        ret.put("file_abs_path", excel_file.getAbsolutePath());
        ret.put("status", "success");

        lock.unlock();
        return ret;
    }

    public HashMap<String, String> validate(CommonsMultipartFile file, List<String> train_cols, String test_col) throws IOException, InvalidFormatException {
        HashMap<String, String> ret = this.validate(file);

        if (ret.get("status") != "success" || !ret.containsKey("cols"))
            return ret;

        Set<String> set = new HashSet(Arrays.asList(ret.get("cols").split(",")));
        for (String col : train_cols) {
            if (!set.contains(col)) {
                ret.put("status", "failed");
                ret.put("msg", "predicting file did not hava train col:" + col);
                return ret;
            }
        }
        return ret;
    }

    public HashMap<String, String> start_train(String train_file_path, List<String> train_cols, String test_col) throws IOException {
        lock.lock();
        HashMap<String, String> ret = new HashMap<>();

        File file = new File(train_file_path);
        System.out.println("train_file_path --- " + file.getAbsoluteFile());
        String abs_path = file.getAbsolutePath();

        try {
            System.out.println("start");
            System.out.println("PYTHON_TRAINING_SCRIPT_PATH --- " + PYTHON_TRAINING_SCRIPT_PATH);

            String tmp_version_str = String.valueOf(version.incrementAndGet());
            //构造参数
            String[] args1 = new String[train_cols.size() + 5];
            args1[0] = "python";
            args1[1] = PYTHON_TRAINING_SCRIPT_PATH;
            args1[2] = abs_path;
            for (int i = 0; i < train_cols.size(); i++) {
                args1[i + 3] = train_cols.get(i);
            }
            args1[args1.length - 2] = test_col;
            args1[args1.length - 1] = tmp_version_str;

            //输出命令行内容
            System.out.println(StringUtils.join(args1, " "));

            //执行Python训练脚本
            Process pr = Runtime.getRuntime().exec(args1);
            AtomicReference<Process> ref = new AtomicReference<>(pr);
            AtomicReference<String> model_file_path = new AtomicReference<>("");

            //接收错误信息
            new Thread(() -> {
                BufferedReader out = null;
                try {
                    out = new BufferedReader(new InputStreamReader(ref.get().getErrorStream()));
                    String thread_name = Thread.currentThread().getName();
                    String thread_version = thread_name.substring(thread_name.lastIndexOf("_") + 1, thread_name.length());
                    String line;
                    while ((line = out.readLine()) != null) {

                        logger.error("Thread " + thread_version + " error info : " + line);
                        System.out.println("Thread " + thread_version + " error info : " + line);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }, "error_output_" + tmp_version_str).start();

            //接收标准信息
            new Thread(() -> {
                BufferedReader in = null;
                try {
                    in = new BufferedReader(new InputStreamReader(ref.get().getInputStream()));
                    String thread_name = Thread.currentThread().getName();
                    String thread_version = thread_name.substring(thread_name.lastIndexOf("_") + 1, thread_name.length());
                    String line;
                    while ((line = in.readLine()) != null) {
                        logger.info("Thread " + thread_version + " error info : " + line);
                        System.out.println("Thread " + thread_version + " standard info : " + line);
                        if (line.indexOf("tujunxiongniubi") != -1 && line.indexOf("menjinsuoniubi") != -1) {
                            model_file_path.set(line.substring(15, line.indexOf("menjinsuoniubi")));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }, "standard_output_" + tmp_version_str).start();

            int status = pr.waitFor();
            System.out.println("training status:" + status);
            if (status != 0) {
                ret.put("status", "failed");
                ret.put("msg", "Internal error happens when python scripts running");
            }
            if (ret.size() == 0) {
                ret.put("model_file_path", model_file_path.get());
                ret.put("status", "success");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        lock.unlock();
        return ret;
    }

    public HashMap<String, String> start_predict(CommonsMultipartFile file, String model_file_path, String test_file_path, List<String> train_cols, String test_col) throws IOException, InvalidFormatException {
        lock.lock();
        // 对测试文件 进行校验
        HashMap<String, String> ret = this.validate(file, train_cols, test_col);

        if (!ret.getOrDefault("status", "failed").equals("success"))
            return ret;
        else {
            try {
                System.out.println("start");
                System.out.println("PYTHON_PREDICTING_SCRIPT_PATH --- " + PYTHON_PREDICTING_SCRIPT_PATH);

                String tmp_version_str = String.valueOf(version.incrementAndGet());
                String[] args1 = new String[train_cols.size() + 6];
                args1[0] = "python";
                args1[1] = PYTHON_PREDICTING_SCRIPT_PATH;
                args1[2] = model_file_path;                    //模型文件
                args1[3] = ret.get("file_abs_path");                    //测试文件

                for (int i = 0; i < train_cols.size(); i++) {
                    args1[i + 4] = train_cols.get(i);
                }
                args1[args1.length - 2] = test_col;
                args1[args1.length - 1] = tmp_version_str;

                System.out.println(StringUtils.join(args1, " "));
                //执行Python训练脚本
                Process pr = Runtime.getRuntime().exec(args1);

                AtomicReference<String> result_file_path = new AtomicReference<>("");
                AtomicReference<Process> ref = new AtomicReference<>(pr);
                //接收错误信息
                new Thread(() -> {
                    BufferedReader out = null;
                    try {
                        out = new BufferedReader(new InputStreamReader(ref.get().getErrorStream()));
                        String thread_name = Thread.currentThread().getName();
                        String thread_version = thread_name.substring(thread_name.lastIndexOf("_") + 1, thread_name.length());
                        String line;
                        while ((line = out.readLine()) != null) {
                            logger.info("Thread " + thread_version + " error info : " + line);
                            System.out.println("Thread " + thread_version + " error info : " + line);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }, "error_output_" + tmp_version_str).start();

                //接收标准信息
                new Thread(() -> {
                    BufferedReader in = null;
                    try {
                        in = new BufferedReader(new InputStreamReader(ref.get().getInputStream()));
                        String thread_name = Thread.currentThread().getName();
                        String thread_version = thread_name.substring(thread_name.lastIndexOf("_") + 1, thread_name.length());
                        String line;
                        while ((line = in.readLine()) != null) {
                            logger.info("Thread " + thread_version + " standard info : " + line);

                            System.out.println("Thread " + thread_version + " standard info : " + line);
                            if (line.indexOf("tujunxiongniubi") >= 0 && line.lastIndexOf("menjinsuoniubi") > 0) {
                                result_file_path.set(line.substring(15, line.lastIndexOf("menjinsuoniubi")));
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }, "standard_output_" + tmp_version_str).start();


                int status = pr.waitFor();
                System.out.println(status);
                if (status != 0) {
                    ret.put("status", "failed");
                    ret.put("msg", "Internal error happens when python scripts running");
                }
                if (!ret.getOrDefault("status", "success").equals("failed")) {
                    ret.put("result_file_path", result_file_path.get());
                    ret.put("status", "success");
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            lock.unlock();
            return ret;
        }
    }
}
